#include "../apue.h"
#include "../ftp.h"

void tcp_send(struct execute_result ser_to_cli)
{
	int sockfd, confd;
	int ret, on = 1;
	char buffer[256];
	struct sockaddr_in selfaddr, conaddr;
	socklen_t conlen = sizeof(conaddr);
	memset(buffer, 0, sizeof(buffer));
	bzero(&conaddr, sizeof(conaddr));

	sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sockfd < 0) {
		perror("socket");
		exit(1);
	}

	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int));

	bzero(&selfaddr, sizeof(selfaddr));
	selfaddr.sin_family = AF_INET;
	selfaddr.sin_port = htons(1234);
	selfaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	ret = bind(sockfd, (struct sockaddr *)&selfaddr, sizeof(selfaddr));
	if (ret < 0) {
		perror("bind");
		goto ERR;
	}

	ret = listen(sockfd, 10);
	if (ret < 0) {
		perror("lesten");
		goto ERR;
	}
	confd = accept(sockfd, (struct sockaddr *)&conaddr, &conlen);
	if (confd < 0) {
		perror("accept");
		goto ERR;
	}

	ret = write(confd, &ser_to_cli, sizeof(ser_to_cli));
	if (ret < 0) {
		perror("write");
		goto ERR;
	}

 ERR:
	close(sockfd);

}

void tcp_rec(char *buffer)
{
	int sockfd, confd;
	int ret, on = 1;
	//char buffer[256];
	struct sockaddr_in selfaddr, conaddr;
	socklen_t conlen = sizeof(conaddr);
	memset(buffer, 0, sizeof(buffer));
	bzero(&conaddr, sizeof(conaddr));

	sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sockfd < 0) {
		perror("socket");
		exit(1);
	}

	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int));

	bzero(&selfaddr, sizeof(selfaddr));
	selfaddr.sin_family = AF_INET;
	selfaddr.sin_port = htons(1234);
	selfaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	ret = bind(sockfd, (struct sockaddr *)&selfaddr, sizeof(selfaddr));
	if (ret < 0) {
		perror("bind");
		goto ERR;
	}

	ret = listen(sockfd, 10);
	if (ret < 0) {
		perror("lesten");
		goto ERR;
	}
	confd = accept(sockfd, (struct sockaddr *)&conaddr, &conlen);
	if (confd < 0) {
		perror("accept");
		goto ERR;
	}
	int n;
	while (1) {
		memset(&buffer, 0, sizeof(buffer));
		n = read(confd, &buffer, sizeof(buffer));
		if (n < 0) {
			perror("read");
			goto ERR;
		}

	}

 ERR:
	close(sockfd);
	exit(0);
}

void login_func(struct login_data *shm_memory,
		struct rec_to_manage rec_info_to_manage)
{
	char *pwd = NULL;
	struct spwd *pshadow = NULL;
	char buffer[128];
	char *passwd = NULL;
	int ret, i;
	struct execute_result ser_to_cli;
	memset(&ser_to_cli, 0, sizeof(ser_to_cli));
	passwd = rec_info_to_manage.info.cmd_arg.login.login_passwd;
	pshadow = getspnam(rec_info_to_manage.info.cmd_arg.login.login_user);
	pwd = (char *)crypt(passwd, pshadow->sp_pwdp);
	ret = strcmp(pwd, pshadow->sp_pwdp);
	if (ret == 0) {
		sprintf(ser_to_cli.execute_data, "login successful");
		tcp_send(ser_to_cli);
	} else {
		for (i = 0; i < 50; i++) {
			if (rec_info_to_manage.addr.sin_addr.s_addr ==
			    shm_memory[i].rec_info.addr.sin_addr.s_addr) {
				if (rec_info_to_manage.info.cli_pid ==
				    shm_memory[i].rec_info.info.cli_pid) {
					bzero(&shm_memory[i],
					      sizeof(shm_memory[i]));
				}
			}
		}
		sprintf(ser_to_cli.execute_data, "login failed");
		tcp_send(ser_to_cli);
	}
}

void put_func(struct rec_to_manage rec_info_to_manage)
{
	char pathname[100];
	char *fname;
	char buffer[256];
	int fid;
	struct execute_result ser_send_to_cli;

	memset(&ser_send_to_cli, 0, sizeof(ser_send_to_cli));
	strcmp(pathname, rec_info_to_manage.info.cmd_arg.cmd_pathname);
	fname = basename(pathname);
	/*client put a file to server, server create a file to save info */
	fid = open(fname, O_CREAT | O_EXCL | O_WRONLY);
	while (1) {
		bzero(buffer, sizeof(buffer));
		tcp_rec(buffer);
		write(fid, buffer, strlen(buffer) + 1);
		if (strlen(buffer) == 0) {
			break;
		}
	}
	sprintf(ser_send_to_cli.execute_data, "put successful");
	tcp_send(ser_send_to_cli);
	exit(0);
}

/*client download file*/
void get_func(struct rec_to_manage rec_info_to_manage)
{
	char pathname[100];
	int fid;
	int n;
	struct execute_result ser_send_to_cli;

	memset(&ser_send_to_cli, 0, sizeof(ser_send_to_cli));
	strcpy(pathname, rec_info_to_manage.info.cmd_arg.cmd_pathname);
	fid = open(pathname, O_RDONLY);
	while (1) {
		memset(&ser_send_to_cli, 0, sizeof(ser_send_to_cli));
		n = read(fid, ser_send_to_cli.execute_data,
			 sizeof(ser_send_to_cli.execute_data));
		if (n <= 0) {
			break;
		}
		tcp_send(ser_send_to_cli);
	}
	memset(&ser_send_to_cli, 0, sizeof(ser_send_to_cli));
	sprintf(ser_send_to_cli.execute_data, "get successful");
	tcp_send(ser_send_to_cli);

}

void quit_func(struct login_data *shm_memory,
	       struct rec_to_manage rec_info_to_manage)
{
	struct execute_result ser_send_to_cli;
	//struct login_data shm_memory[50];
	int i;
	memset(&ser_send_to_cli, 0, sizeof(ser_send_to_cli));
	sprintf(ser_send_to_cli.execute_data, "good bye");
	tcp_send(ser_send_to_cli);
	for (i = 0; i < 50; i++) {
		if (rec_info_to_manage.addr.sin_addr.s_addr ==
		    shm_memory[i].rec_info.addr.sin_addr.s_addr) {
			if (rec_info_to_manage.info.cli_pid ==
			    shm_memory[i].rec_info.info.cli_pid) {
				bzero(&shm_memory[i], sizeof(shm_memory[i]));
				unlink(shm_memory[i].pid_fifo);
				break;
			}
		}
	}
	exit(0);
}

void cd_func(struct rec_to_manage rec_info_to_manage)
{
	char pathname[100];
	memset(&rec_info_to_manage, 0, sizeof(rec_info_to_manage));	// should not clear, pathname?
	sprintf(pathname, rec_info_to_manage.info.cmd_arg.cmd_pathname);
	chdir(pathname);
	return;
}

void ls_func(struct rec_to_manage rec_info_to_manage)
{
	char pathname[100];
	struct execute_result ser_to_cli;
	int fid;
	int n;
	int pid;
	memset(&rec_info_to_manage, 0, sizeof(rec_info_to_manage));
	sprintf(pathname, rec_info_to_manage.info.cmd_arg.cmd_pathname);
	char *cmd[] = { "ls", "-l", pathname, NULL };
	fid = open("/home/serval/ls_file", O_CREAT | O_WRONLY | O_TRUNC);
	if (pid == 0) {
		dup2(fid, 1);
		execvp("ls", cmd);
	} else {
		//wait(NULL);	// wait which child process? forgot to fork()?
		close(fid);
		fid = open("/home/serval/ls_file", O_RDONLY);
		while (1) {
			memset(&ser_to_cli, 0, sizeof(ser_to_cli));
			n = read(fid, ser_to_cli.execute_data, sizeof(ser_to_cli.execute_data));
			if (n == 0) {
				close(fid);
				break;
			}
			tcp_send(ser_to_cli);
		}
	}

	return;
}

void mkdir_func(struct rec_to_manage rec_info_to_manage)
{
	char pathname[100];
	char *dname = NULL;
	int ret;
	//struct execute_result execute;
	memset(&dname, 0, sizeof(dname));
	memset(&pathname, 0, sizeof(pathname));
	sprintf(pathname, rec_info_to_manage.info.cmd_arg.cmd_pathname);
	dname = dirname(pathname);
	ret = mkdir(dname, 0777);
	if (ret < 0) {
		perror("mkdir");
		exit(0);
	}
	return;
}

void touch_func(struct rec_to_manage rec_info_to_manage)
{
	char pathname[100];
	char *fname = NULL;
	int fid;
	memset(&pathname, 0, sizeof(pathname));
	memset(&fname, 0, sizeof(fname));
	sprintf(pathname, rec_info_to_manage.info.cmd_arg.cmd_pathname);
	fname = basename(pathname);
	fid = open(fname, O_CREAT | O_EXCL);
	if (fid < 0) {
		perror("open");
		exit(0);
	}

	return;
}

void deal_func(struct login_data shm_memory,
	       struct rec_to_manage rec_info_to_manage)
{
	struct execute_result ser_to_cli;
	CMD op;
	while (1) ;
	{
		switch (op) {
		case LOGIN:
			login_func(&shm_memory, rec_info_to_manage);
			break;
		case QUIT:
			quit_func(&shm_memory, rec_info_to_manage);
			break;
		case CD:
			//rec_info_to_manage.info.cmd_pathname = pathname;
			cd_func(rec_info_to_manage);
			break;
		case LS:
			ls_func(rec_info_to_manage);
			break;
		case PWD:
			pwd_func(rec_info_to_manage);
			break;
		case GET:
			get_func(rec_info_to_manage);
			break;
		case PUT:
			put_func(rec_info_to_manage);
			break;
		case MKDIR:
			mkdir_func(rec_info_to_manage);
			break;
		case TOUCH:
			touch_func(rec_info_to_manage);
			break;
		default:
			memset(&ser_to_cli, 0, sizeof(ser_to_cli));
			sprintf(ser_to_cli.execute_data, " ?Invalid command");
			tcp_send(ser_to_cli);
		}
	}

	return;
}
